package com.excel.export.util;

import com.excel.export.entity.BackLogBean;
import org.apache.commons.collections4.ListUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;


/**
 * Excel导出的工具类
 * <p>
 * POI各个工具的信息：
 * HSSFWorkbook:是操作Excel2003以前（包括2003）的版本，扩展名是.xls；
 * XSSFWorkbook:是操作Excel2007的版本，扩展名是.xlsx；
 * 从POI 3.8版本开始，提供了一种基于XSSF的低内存占用的API----SXSSF
 * 针对 SXSSF Beta 3.8下，会有临时文件产生，根据实际情况，使用workbook.dispose()删除临时文件
 * <p>
 * 区别：
 * 与XSSF的对比
 * 在一个时间点上，只可以访问一定数量的数据
 * 不再支持Sheet.clone()
 * 不再支持公式的求值
 * 在使用Excel模板下载数据时将不能动态改变表头，因为这种方式已经提前把excel写到硬盘的了就不能再改了
 *
 * @author ZH
 * @date 2022年01月16日 16:31:00
 * @version: 1.0.0
 */
public class ExcelUtil {
    /**
     * workbook内存缓冲行数，默认值100
     */
    private static final int BUFFER_LINE = 100;
    /**
     * Sheet阈值，单个Sheet可写入行数的最大值
     */
    private static final int SHEET_THRESHOLD = 50000;
    /**
     * sheet名称前缀
     */
    private static final String SHEET_PREFIX = "sheet";
    public static final String ROW_MERGE = "row_merge";
    public static final String COLUMN_MERGE = "column_merge";
    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm";
    private static final int CELL_OTHER = 0;
    private static final int CELL_ROW_MERGE = 1;
    private static final int CELL_COLUMN_MERGE = 2;

    private static String formatDate(Date date) {
        if (date == null) {
            return "";
        }
        SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);
        return format.format(date);
    }

    /**
     * 导出Excel
     *
     * @return excel的workbook
     */
    public static SXSSFWorkbook export(List<BackLogBean> beans) {
        //SXSSFWorkbook默认创建XSSFWorkbook
        SXSSFWorkbook workbook = new SXSSFWorkbook(BUFFER_LINE);
        //是否对临时文件进行压缩，压缩会使导出时间延长
        workbook.setCompressTempFiles(false);
        //是否需要多个Sheet
        if (beans.size() <= SHEET_THRESHOLD) {
            SXSSFSheet sheet = workbook.createSheet(SHEET_PREFIX);
            // 设置表格列宽度（默认为15个字节）
            sheet.setDefaultColumnWidth(25);
            setValueOfSheet(workbook, sheet, beans);
        } else {
            //多个sheet暂时用不到
            List<List<BackLogBean>> partition = ListUtils.partition(beans, SHEET_THRESHOLD);
            for (int j = 0; j < partition.size(); j++) {
                SXSSFSheet sheet = workbook.createSheet(SHEET_PREFIX + j);
                // 设置表格列宽度（默认为15个字节）
                sheet.setDefaultColumnWidth(25);
                setValueOfSheet(workbook, sheet, partition.get(j));
            }
        }
        return workbook;
    }

    /**
     * 将数据写入Sheet，每个Sheet的具体处理在此方法中，根据实际情况改变
     *
     * @param sheet excel的Sheet
     */
    private static void setValueOfSheet(SXSSFWorkbook workbook, SXSSFSheet sheet, List<BackLogBean> backLog) {
        for (int i = 0; i <= backLog.size(); i++) {
            SXSSFRow row = sheet.createRow(i);
            CellStyle style = workbook.createCellStyle();
            if (i == 0) {
                //修改工作簿第一行样式(灰色)
                style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                style.setAlignment(HorizontalAlignment.CENTER);
                style.setFillForegroundColor(IndexedColors.GREY_40_PERCENT.index);
                setCellValue(row.createCell(0), "待办内容", style);
                setCellValue(row.createCell(1), "发起人", style);
                setCellValue(row.createCell(2), "发起时间", style);
                setCellValue(row.createCell(3), "接收人", style);
            } else {
                // 设置表身背景色（默认色）
                style.setAlignment(HorizontalAlignment.CENTER);
                style.setVerticalAlignment(VerticalAlignment.CENTER);
                setCellValue(row.createCell(0), backLog.get(i - 1).getContent(), style);
                setCellValue(row.createCell(1), backLog.get(i - 1).getSponsor(), style);
                setCellValue(row.createCell(2), backLog.get(i - 1).getRiseTime(), style);
                setCellValue(row.createCell(3), backLog.get(i - 1).getRecipient(), style);
            }

        }
    }


    private static int setCellValue(Cell cell, Object o, CellStyle style) {
        // 设置样式
        cell.setCellStyle(style);
        // 数据为空时
        if (o == null) {
            cell.setCellType(CellType.STRING);
            cell.setCellValue("");
            return CELL_OTHER;
        }
        // 是否为字符串
        if (o instanceof String) {
            String s = o.toString();
            if (isNumeric(s)) {
                cell.setCellType(CellType.NUMERIC);
                cell.setCellValue(Double.parseDouble(s));
                return CELL_OTHER;
            } else {
                cell.setCellType(CellType.STRING);
                cell.setCellValue(s);
            }
            if (s.equals(ROW_MERGE)) {
                return CELL_ROW_MERGE;
            } else if (s.equals(COLUMN_MERGE)) {
                return CELL_COLUMN_MERGE;
            } else {
                return CELL_OTHER;
            }
        }
        // 是否为字符串
        if (o instanceof Integer || o instanceof Long || o instanceof Double || o instanceof Float) {
            cell.setCellType(CellType.NUMERIC);
            cell.setCellValue(Double.parseDouble(o.toString()));
            return CELL_OTHER;
        }
        // 是否为Boolean
        if (o instanceof Boolean) {
            cell.setCellType(CellType.BOOLEAN);
            cell.setCellValue((Boolean) o);
            return CELL_OTHER;
        }
        // 如果是BigDecimal，则默认3位小数
        if (o instanceof BigDecimal) {
            cell.setCellType(CellType.NUMERIC);
            cell.setCellValue(((BigDecimal) o).setScale(3, RoundingMode.HALF_UP).doubleValue());
            return CELL_OTHER;
        }
        // 如果是Date数据，则显示格式化数据
        if (o instanceof Date) {
            cell.setCellType(CellType.STRING);
            cell.setCellValue(formatDate((Date) o));
            return CELL_OTHER;
        }
        // 如果是其他，则默认字符串类型
        cell.setCellType(CellType.STRING);
        cell.setCellValue(o.toString());
        return CELL_OTHER;
    }

    private static boolean isNumeric(String str) {
        if ("0.0".equals(str)) {
            return true;
        }
        for (int i = str.length(); --i >= 0; ) {
            if (!Character.isDigit(str.charAt(i))) {
                return false;
            }
        }
        return true;
    }
}
